package com.zhangkeyou.map.mapweb.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zhangkeyou.map.mapweb.domain.entity.LoginUser;
import com.zhangkeyou.map.mapweb.domain.entity.Status;
import com.zhangkeyou.map.mapweb.domain.entity.Userinfo;
import com.zhangkeyou.map.mapweb.mapper.MenuMapper;
import com.zhangkeyou.map.mapweb.mapper.UserinfoMapper;
import com.zhangkeyou.map.mapweb.util.JwtUtil;
import com.zhangkeyou.map.mapweb.util.MyUtils;
import com.zhangkeyou.map.mapweb.util.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Slf4j
public class UserinfoService implements UserDetailsService {
    @Autowired
    private UserinfoMapper userinfoMapper;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private MenuMapper menuMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LambdaQueryWrapper<Userinfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Userinfo::getUsername,username);

        Userinfo userinfo = userinfoMapper.selectOne(wrapper);
        if(Objects.isNull(userinfo)){
            throw new RuntimeException("用户名或密码不正确");
        }
        //TODO 查询对应权限信息
        List<String> authorities = menuMapper.selectPermsByUserId(userinfo.getId());
        return new LoginUser(userinfo,authorities);
    }

    public Status getAuthorities(){
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(Objects.isNull(loginUser))
            throw new RuntimeException("未登录");
        String userId = loginUser.getUserinfo().getId();
        List<String> authorities = menuMapper.selectPermsByUserId(userId);
        return new Status<>(200,authorities,"success");

    }

    /**
     * 登录
     * @param userinfo
     * @return 登录成功，返回用户信息
     */
    public Status login(Userinfo userinfo){
        //通过用户及密码获取认证
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userinfo.getUsername(), userinfo.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authentication);
        if(Objects.isNull(authenticate)){
            throw new RuntimeException("账户或密码错误");
        }
        //获取通过认证后的User信息
        LoginUser loginUser = (LoginUser)authenticate.getPrincipal();


        //保存用户id信息保存到redis
        Map<String,String> resultMap = saveUserinfoInRedis(loginUser);
        log.debug("username:"+loginUser.getUsername()+"-->登录成功:"+resultMap.toString());
        return new Status(200,resultMap,"success");


/*        QueryWrapper<Userinfo> wrapper = new QueryWrapper<>();
        wrapper.eq("username",userinfo.getUsername()).eq("password",userinfo.getPassword());
        Userinfo userinfo1 = userinfoMapper.selectOne(wrapper);
        if(userinfo1==null)
            return null;
        if(userinfo1.getToken()==null){
            userinfo1.setToken(UUID.randomUUID().toString());
            userinfoMapper.update(userinfo1,wrapper);
        }
        return userinfo1==null?null:userinfo1;*/
    }

    public Status register(Userinfo userinfo){
        if(exitsUserinfo(userinfo)){
            return new Status<>(400,null,"用户名重复");
        }
        userinfo.setPassword(passwordEncoder.encode(userinfo.getPassword()));

        userinfoMapper.insert(userinfo);
        return new Status<>(200,null,"注册成功");
    }

    /**
     * 判断用户名是否存在
     * @param userinfo
     * @return
     */
    private Boolean exitsUserinfo(Userinfo userinfo){
        LambdaQueryWrapper<Userinfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Userinfo::getUsername,userinfo.getUsername());
        Long count = userinfoMapper.selectCount(wrapper);
        return count >= 1;
    }

    public List<Userinfo> getInfoList(){
        List<Userinfo> userinfos = userinfoMapper.selectList(null);
        return userinfos;
    }

    public String selectNameByName(String token){
        return null;
    }

    public Status logout() {
        //获取SecurityContextHolder中的用户id
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String userId = loginUser.getUserinfo().getId();

        redisCache.deleteObject(MyUtils.TOKEN_NAME_IN_REDIS_PRE+userId);

        return new Status<>(200,null,"注销成功");
    }

    public Status getUserInfo(String userId) {
        LambdaQueryWrapper<Userinfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Userinfo::getId,userId);
        Userinfo userinfo = userinfoMapper.selectOne(wrapper);
        if(Objects.isNull(userinfo)){
            return new Status<>(400,"","未查到数据");
        }else{
            return new Status<>(200,userinfo,"success");
        }
    }

    public Status updateHeader(String header) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String userId = loginUser.getUserinfo().getId();
        int count = userinfoMapper.updateHeader(userId,header);
        if(count>0) {
            saveUserinfoInRedis(loginUser,true);
            return new Status(200, "", "success");
        }
        else
            return new Status(400,"","未找到用户");
    }

    /**
     *
     * @param loginUser 要保存登录信息
     * @param isUpdateUserInfo 是否重新加载用户信息
     */
    private Map<String,String> saveUserinfoInRedis(LoginUser loginUser,boolean isUpdateUserInfo){
        if(isUpdateUserInfo){
            LambdaQueryWrapper<Userinfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Userinfo::getId,loginUser.getUserinfo().getId());
            Userinfo userinfo = userinfoMapper.selectOne(wrapper);
            if(userinfo!=null){
                loginUser.setUserinfo(userinfo);
            }else{
                log.error("重新加载用户失败");
            }
        }

        String userId = loginUser.getUserinfo().getId();
        String jwt = JwtUtil.createJWT(userId.toString());
        Map<String,String> map = new HashMap<>();
        map.put(MyUtils.TOKEN_NAME_IN_HEADER,jwt);
        redisCache.setCacheObject(MyUtils.TOKEN_NAME_IN_REDIS_PRE+loginUser.getUserinfo().getId(),loginUser);
        return map;
    }

    /**
     * 保存用户信息到redis ， 不重新加载数据库中的内容
     * @param loginUser 要保存的用户信息
     * @return 返回要给前端的信息
     */
    private Map<String,String> saveUserinfoInRedis(LoginUser loginUser){
        return saveUserinfoInRedis(loginUser,false);
    }

    public Status updatePassword(String password) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String userId = loginUser.getUserinfo().getId();
        String encodePassword = passwordEncoder.encode(password);
        int count = userinfoMapper.updatePassword(userId,encodePassword);
        if(count>0) {
            logout();
            return new Status(200, "", "success");
        }
        else
            return new Status(400,"","未找到用户");

    }
}
